Trading





Kerry Back

Workflow

  • Spreadsheets
    • Positions (initially all zero)
    • Ranks (from train and predict)
    • Prices (most recent stock prices)
  • (Positions + Ranks + Prices) \(\rightarrow\) Trades
  • (Positions + Trades) \(\rightarrow\) updated Positions

Initial portfolio

  • Cash to invest
  • Select numstocks, equal weight (for example)
  • From Ranks, get top numstocks
  • (cash/numstocks) = $ per stock
  • From prices, calculate shares = $ per stock / price
  • \(\Rightarrow\) Trades \(\Rightarrow\) Positions

Trading

  • Update cash = cash + interest
  • New Ranks (no longer holding just top stocks)
  • New Prices (no longer equally weighted)
  • Need to decide how (Positions + Ranks + Prices) \(\rightarrow\) Trades

Possible strategy

  • If new rank is below some threshold, sell all shares
  • Otherwise, if new weight is above some threshold, sell some shares
  • Invest updated cash + proceeds from sales as
    • Start with best stock with no current holding
    • Buy enough shares to hit target weight
    • Then next best stock, etc., until out of cash

Python digression

  • Suppose ser is a pandas series. What is
2*ser
  • What is
2 > 1
  • What is
ser > 1
  • If x is another pandas series with the same index, what is
x[ser > 1]

  • What is
10 if 2 > 1 else 20
  • If ser is a pandas series, what is
np.where(ser>1, 10, 20)

Example

  • 26 stocks in universe. Current ranks and prices:
ranks = pd.read_excel("files/ranks0.xlsx", index_col=0)
ranks = ranks.squeeze()

prices = pd.read_excel("files/prices0.xlsx", index_col=0)
prices = prices.squeeze()
  • $100,000 to invest, want to hold 5 stocks equally weighted
equity = cash = 100000
weight = 0.2

Initial portfolio

buys = ranks<=5
buyamounts = (weight*equity/prices).astype(int)

trades = np.where(buys, buyamounts, 0)
trades = pd.Series(trades, index=ranks.index)

positions = trades
cash -= (trades*prices).sum()

Here are the trades:

A      0
B      0
C      0
D    810
E      0
F      0
G      0
H    294
I    573
J    244
K      0
L      0
M      0
N      0
O    481
P      0
Q      0
R      0
S      0
T      0
U      0
V      0
W      0
X      0
Y      0
Z      0
dtype: int32

Update ranks, prices, and equity

  • Update ranks and prices (normally run model and check prices):
ranks = pd.read_excel("files/ranks1.xlsx", index_col=0)
ranks = ranks.squeeze()

prices = pd.read_excel("files/prices1.xlsx", index_col=0)
prices = prices.squeeze()
  • Update portfolio value (normally would update from broker):
equity = cash + (positions*prices).sum()
weights = (positions*prices) / equity

Sells

sellall = (ranks>10) & (positions !=0)
sellallamounts = positions
trades = - np.where(sellall, sellallamounts, 0)

sellsome = (ranks<=10) & (weights>0.2)
sellsomeamounts = (equity*(weights-0.2)/prices).astype(int)
trades -= np.where(sellsome, sellsomeamounts, 0)

trades = pd.Series(trades, index=ranks.index)
cash -= (trades*prices).sum()

Buys

  • Would like a list of the tickers not currently owned, starting with best stock.
  • Can get it like this:
tickers = ranks[positions==0].sort_values().index.to_list()
  • What does tick = tickers.pop(0) do?
    • tick is the first ticker in the list
    • tick is removed from the list (popped off)
  • Want to loop over tickers until cash is exhausted.
  • Use “while loop”: while cash >= …

  • while loop:
while cash >= weight*equity:
    tick = tickers.pop(0)
    trades[tick] = int(weight*equity/prices[tick])
    cash -= trades[tick]*prices[tick]
  • Spend remaining cash on one more stock.
tick = tickers.pop(0)
trades[tick] = int(cash/prices[tick])
cash -= trades[tick]*prices[tick]

positions += trades

Here are the trades:

A      0
B    401
C      0
D    -40
E      0
F      0
G      0
H      0
I      0
J     -6
K      0
L      0
M      0
N     21
O   -481
P      0
Q      0
R      0
S      0
T      0
U      0
V      0
W      0
X      0
Y      0
Z      0
dtype: int32